<?php
/**
 * User: Administrator
 * Time: 2015/10/28 12:54
 */

//1、 把以下数组筛选出ip为内网的元素，根据ip合并，count值相加（结果仍存在count内），最后按照count值逆序排序。
$items = [
	["ip" => "10.1.1.3", "count" => 123],
	["ip" => "210.33.182.1", "count" => 34],
	["ip" => "82.12.193.2", "count" => 2],
	["ip" => "10.2.124.1", "count" => 20],
	["ip" => "10.1.1.3", "count" => 29],
	["ip" => "101unknow", "count" => 345],
	["ip" => "10.2.124.1", "count" => 20],
	["ip" => "10.80.19.92", "count" => 20],
];

$ips    = array();
$counts = array();
//合并数组
foreach ($items as $item) {
	if (substr($item['ip'], 0, 3) != '10.') {
		continue;
	}
	if (array_key_exists($item['ip'], $ips)) {
		$ips[$item['ip']]['count'] += $item['count'];
	} else {
		$ips[$item['ip']] = $item;
	}
	$counts[$item['ip']] = $ips[$item['ip']]['count'];
}
$ips = array_values($ips);
//排序
array_multisort($counts, SORT_DESC, $ips);
var_dump($ips);

//2、 已知道docx文件的前4个字节的内容是 504B0304（16进制），写程序判断一个文件是否正常的docx文件?
/**
 * 获取文件扩展名
 * @param $filename
 * @return bool|int|string
 */
function getFileExt($filename) {
	$fh      = fopen($filename, 'r');
	$ext_map = array(
		'docx' => [ //docx文件
			'len' => 4,
			'str' => '504B0304'
		]
	);
	foreach ($ext_map as $k => $v) {
		$head = bin2hex(fread($fh, $v['len']));
		if (strcasecmp($head, $v['str']) === 0) {
			return $k;
		}
	}
	return false;
}

$ext = getFileExt('./test.docx');
if ($ext == 'docx') var_dump('是docx文件');

//3、用xmlreader读取一个超过1G的大xml文件（大的xml文件自己造出来）。

/**
 * 生成指定尺寸的xml文件
 * @param $filename
 * @param $size
 */
function generateXmlFile($filename, $size) {
	$test  = fopen($filename, 'w');
	$start = '<?xml version="1.0" encoding="UTF-8"?><collection>';
	$block = '<cd><title type="hk151">港台明星</title><artist>刘德华</artist><year>1995475</year></cd>';
	echo '开始生成xml文件' . PHP_EOL;
	fwrite($test, $start);
	while (true) {
		fwrite($test, $block);
		$current_size = filesize($filename);
		clearstatcache();
		echo $current_size . ' / ' . $size . PHP_EOL;
		if ($current_size > $size) {
			break;
		}
	};
	$end = "  </collection>";
	fwrite($test, $end);
	fclose($test);
	echo 'xml文件生成成功' . PHP_EOL;
}

//生成1G的xml文件
generateXmlFile('./test.xml', 1024 * 1024 * 1024);

//读取xml文件
$reader = new XMLReader();
$reader->open('./test.xml', 'utf-8');
while ($reader->read()) {
	if ($reader->name == "cd" && $reader->nodeType == XMLReader::ELEMENT) {
		while ($reader->read() && $reader->name != "cd") {
			if ($reader->nodeType != XMLReader::ELEMENT) continue;
			$name = $reader->name;
			echo 'name:' . $name . PHP_EOL;
		}

	}
}
$reader->close();


//附加题：
//1、编写一个计算数组维度的函数(如['a']为一维，['a'=>['b']]是二维)。

function array_depth($array) {
	if(!is_array($array)) return 0;
	$max_depth = 1;
	foreach ($array as $value) {
		if (is_array($value)) {
			$depth = array_depth($value) + 1;

			if ($depth > $max_depth) {
				$max_depth = $depth;
			}
		}
	}
	return $max_depth;
}
$array = array(array( array(array(1, 2), 3, 4)));
echo array_depth($array);//4

//2、写一个包含函数为sjmcount的php扩展，sjmcount可以计算数组的长度（与count相同）



//3、利用随机读写文件的原理，编写一个文件分割器，要求能切割和合并文件。
//此题的难点是切割合并文件时，保持内存稳定

/**
 * 切割合并文件
 * Class FileChunk
 */
class FileChunk {

	public $file = array();

	private $maxSize;


	public function __construct() {
		ini_set('memory_limit', '-1');
		$this->maxSize = 1024 * 1024 * 50;
	}

	/**
	 * $maxSize 是每次读取文件时占用的内存，默认50M
	 * 它的值越高，切割或合并文件速度越快，但是内存占用也越高
	 * 可以根据机器配置适当调整
	 * @param $size
	 */
	public function sexMaxSize($size) {
		$this->maxSize = $size;
	}

	/**
	 * 切割文件
	 * @param $filePath //文件名
	 * @param $chunkNum //切割块数
	 */
	public function cut($filePath, $chunkNum) {
		$this->init($filePath);
		$chunks  = $this->getChunks($chunkNum);
		$rowFile = fopen($filePath, 'r');
		foreach ($chunks as $chunk) {
			$size      = $chunk['size'];
			$chunkFile = fopen('./' . $chunk['name'], 'w');
			while (!feof($rowFile)) {
				$data = fread($rowFile, $this->getReadSize($size));
				echo '当前占用内存'.memory_get_usage().PHP_EOL;
				$size -= $this->getReadSize($size);
				fwrite($chunkFile, $data);
				if ($size <= 0) {
					break;
				}
			}
			echo $chunk['name'] . '生成成功,文件大小' . $chunk['size'] . PHP_EOL;
			fclose($chunkFile);
		}
		fclose($rowFile);
	}

	/**
	 * 合并文件
	 * @param array $files
	 * @param       $newFilename
	 */
	public function merge($files = array(), $newFilename) {
		$newFile = fopen($newFilename, 'w');
		foreach ($files as $file) {
			if (!file_exists($file)) die('文件不存在');
			$size      = filesize($file);
			$chunkFile = fopen($file, 'r');
			while (!feof($chunkFile)) {
				$data = fread($chunkFile, $this->getReadSize($size));
				echo '当前占用内存'.memory_get_usage().PHP_EOL;
				fwrite($newFile, $data);
			}
			echo $file . ' 已合并' . PHP_EOL;
		}
		echo $newFilename . '已经全部合并成功';
	}

	/**
	 * 获取文件基本信息
	 */
	private function init($filePath) {
		$file       = new SplFileInfo($filePath);
		$this->file = array(
			'file_name'  => $file->getFilename(),
			'isFile'     => $file->isFile(),
			'isReadable' => $file->isReadable(),
			'isWritable' => $file->isWritable(),
			'size'       => $file->getSize(),
		);
		if (!$this->file['isFile']) {
			die('不是一个有效的文件');
		}
		if (!$this->file['isReadable'] || !$this->file['isWritable']) {
			die('文件不可读写');
		}
		echo '文件总大小：' . $this->file['size'] . '字节' . PHP_EOL;
	}


	/**
	 * 计算每块文件的大小
	 * @param $chunkNum
	 * @return array
	 */
	private function getChunks($chunkNum) {
		$count = 0;
		while ($this->file['size'] % $chunkNum != 0) {
			$this->file['size']--;
			$count++;
		}
		$chunks = array();
		for ($i = 0; $i < $chunkNum; $i++) {
			$chunks[$i]['name'] = $this->file['file_name'] . '_' . ($i + 1);
			$chunks[$i]['size'] = $this->file['size'] / $chunkNum;
		}

		$chunks[$chunkNum - 1]['size'] += $count;
		return $chunks;
	}


	/**
	 * 计算每次读取的文件大小
	 * @param $size
	 * @return int
	 */
	private function getReadSize($size) {
		if ($size < $this->maxSize) return $size;
		return $this->maxSize;
	}

}

$obj = new FileChunk();
//设置使用内存200M
$obj->sexMaxSize(200 * 1024 * 1024);
//把文件切割成5块
$obj->cut('./hadoop.mp4', 5);
//合并文件
//$obj->merge([
//	'./hadoop.mp4_1',
//	'./hadoop.mp4_2',
//	'./hadoop.mp4_3',
//	'./hadoop.mp4_4',
//	'./hadoop.mp4_5',
//], './newhadoop.mp4');



